<template>
  <div class="container">
    <div class="aside" ref="asideEl">
      <div class="title">可拖动到右侧画布→</div>
      <div
        class="item"
        :class="{ disabled: state.dropedNodes.some((it) => it.data.id === item.id) }"
        v-for="(item, index) in state.nodesList"
        :key="index"
        @mousedown="(e) => startCreateNode(e, item)"
      >
        <div class="left">
          <img :src="item.image" alt="" class="image" />
          <div class="info">
            <div class="name">{{ item.label }}</div>
            <div class="sub">{{ item.subLabel }}</div>
          </div>
        </div>
        <div class="icon">
          <i class="el-icon-caret-right"></i>
        </div>
      </div>
    </div>
    <div class="graph" ref="graphEl"></div>
    <button class="button" @click="exportData">导出数据</button>
  </div>
</template>

<script>
import { Graph, Shape } from "@antv/x6";
// import { Stencil } from "@antv/x6-plugin-stencil";
// import { Transform } from "@antv/x6-plugin-transform";
// import { Scroller } from "@antv/x6-plugin-scroller";
import { Selection } from "@antv/x6-plugin-selection";
import { Snapline } from "@antv/x6-plugin-snapline";
import { Keyboard } from "@antv/x6-plugin-keyboard";
import { Clipboard } from "@antv/x6-plugin-clipboard";
import { History } from "@antv/x6-plugin-history";
import { Dnd } from "@antv/x6-plugin-dnd";
import { register } from "@antv/x6-vue-shape";
import { onMounted, provide, reactive, ref, unref, watch } from "vue";
import Vue from 'vue'
// import { useExchangeData } from "../pinia/pinia";
import customVueNodeVue from "./customVueNode.vue";
export default {
  setup() {
    const state = reactive({
      // 所有可用节点，可通过异步请求获取
      nodesList: [
        {
          id: 1,
          label: "阀门编号",
          subLabel: "阀门名称",
          image:
            "https://img0.baidu.com/it/u=2160901077,1032449260&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=749",
        },
        {
          id: 2,
          label: "Http",
          subLabel: "阀门名称0",
          image:
            "https://pic3.zhimg.com/50/v2-e55fd75f4c808d59624474904ae93039_hd.jpg",
        },
        {
          id: 3,
          label: "Api",
          subLabel: "阀门名称1",
          image:
            "https://img0.baidu.com/it/u=1437927748,1971909415&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500",
        },
        {
          id: 4,
          label: "Sql",
          subLabel: "阀门名称2",
          image:
            "https://img2.baidu.com/it/u=699469868,2952981715&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=403",
        },
        {
          id: 5,
          label: "Clound",
          subLabel: "阀门名称3",
          image:
            "https://ss3.baidu.com/-fo3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/1f178a82b9014a90b02fcd13a9773912b31bee0f.jpg",
        },
        {
          id: 6,
          label: "Mq",
          subLabel: "阀门名称4",
          image:
            "https://img0.baidu.com/it/u=2977762979,44167134&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
        },
      ],
      dropedNodes: [], //已添加到画布中的节点
      refreshData: [],
    });
    const refreshNodesData = () => {
      state.dropedNodes.forEach(item => {
        item.updateData({...item.data,refreshData: state.refreshData})
      })
    }
    // x6的vue节点 vuex等用不了，数据传输就变得很奇葩了
    Vue.__X6Funs = {
      changeData(data, node) {
        console.log(data, "data");
        // 此处接收节点内部 switch开关时候的数据
        // 可以再这里处理逻辑
        node.replaceData(data);
      },
      setRefreshData(data) {
        state.refreshData.push(data);
        if (state.refreshData.length === 2) {
          const [cell1, cell2] = state.refreshData;
          const cell1Data = JSON.parse(JSON.stringify(cell1.data));
          const cell2Data = JSON.parse(JSON.stringify(cell2.data));
          console.log({ cell2Data, cell1Data });
          cell1.replaceData(cell2Data);
          cell2.replaceData(cell1Data);
          state.refreshData = [];
          refreshNodesData()
        }else {
          refreshNodesData()
        }
      },
      getRefreshData() {
        return state.refreshData;
      }
    }
    const asideEl = ref(null),
      graphEl = ref(null);
    let graph = null,
      dnd = null;
    // 节点的连接桩
    const ports = {
      groups: {
        top: {
          position: "top",
          attrs: {
            circle: {
              r: 4,
              magnet: true,
              stroke: "#5F95FF",
              strokeWidth: 1,
              fill: "#fff",
              style: {
                visibility: "hidden",
              },
            },
          },
        },
        right: {
          position: "right",
          attrs: {
            circle: {
              r: 4,
              magnet: true,
              stroke: "#5F95FF",
              strokeWidth: 1,
              fill: "#fff",
              style: {
                visibility: "hidden",
              },
            },
          },
        },
        bottom: {
          position: "bottom",
          attrs: {
            circle: {
              r: 4,
              magnet: true,
              stroke: "#5F95FF",
              strokeWidth: 1,
              fill: "#fff",
              style: {
                visibility: "hidden",
              },
            },
          },
        },
        left: {
          position: "left",
          attrs: {
            circle: {
              r: 4,
              magnet: true,
              stroke: "#5F95FF",
              strokeWidth: 1,
              fill: "#fff",
              style: {
                visibility: "hidden",
              },
            },
          },
        },
      },
      items: [
        {
          group: "top",
        },
        {
          group: "right",
        },
        {
          group: "bottom",
        },
        {
          group: "left",
        },
      ],
    };
    const exportData = () => {
      const jsonData = graph.toJSON();
      console.log(jsonData, "jsonData");
    };
    // 初始化一个节点，如果没有回显数据可以执行该方法。需要在异步判断之后或者按需调用
    const addInitNode = () => {
      // 添加一个初始节点
      graph.addNode({
        data: {
          nodeName: "rootName",
        },
        shape: "circle",
        x: 100,
        y: 40,
        width: 40,
        height: 40,
        ports,
        attrs: {
          circle: {
            fill: "#5F95F0",
            stroke: "#5F95F0",
          },
          text: {
            fill: "#fff",
          },
        },
        label: "总",
      });
    };
    const init = () => {
      // 初始化实例
      graph = new Graph({
        container: unref(graphEl),
        grid: true,
        panning: false,
        autoResize: true,
        mousewheel: {
          enabled: true,
          zoomAtMousePosition: true,
          modifiers: "ctrl",
          minScale: 0.5,
          maxScale: 3,
        },
        connecting: {
          router: "manhattan",
          allowMulti: false, //是否允许在相同的起始节点和终止之间创建多条边，默认为 true。
          allowLoop: false, //是否允许连接到画布空白位置的点，默认为 true。
          allowBlank: false, //是否允许连接到画布空白位置的点，默认为 true。
          allowEdge: false, //是否允许连接到画布空白位置的点，默认为 true。
          connector: {
            name: "rounded",
            args: {
              radius: 8,
            },
          },
          anchor: "center",
          connectionPoint: "anchor",
          snap: {
            radius: 20,
          }, //当 snap 设置为 true 或者 false 代表开启和关闭连线过程中自动吸附，开启时距离目标 50px 是触发吸附。可以通过配置 radius 属性来自定义吸附半径。
          createEdge() {
            return new Shape.Edge({
              // tools: ["button-remove"],
              attrs: {
                line: {
                  stroke: "#A2B1C3",
                  strokeWidth: 2,
                  targetMarker: {
                    name: "block",
                    width: 12,
                    height: 8,
                  },
                },
              },
              zIndex: 0,
            });
          },
          validateConnection({ targetMagnet }) {
            return !!targetMagnet;
          },
        },
        highlighting: {
          magnetAdsorbed: {
            name: "stroke",
            args: {
              attrs: {
                fill: "#5F95FF",
                stroke: "#5F95FF",
              },
            },
          },
        },
      });
      dnd = new Dnd({
        target: graph,
        // 节点放置到画布中的时候
        getDropNode(node) {
          // 存储当前已经放入画布的节点
          state.dropedNodes.push(node);
          return node.clone({ keepId: true });
        },
      });
      // 如果有回显的数据，这里可以初始这个节点。可以根据异步情况判定决定是否执行此函数
      addInitNode();
      // #region 使用插件
      graph
        // 图形变换插件
        // .use(
        //   new Transform({
        //     resizing: true,
        //     rotating: true,
        //   })
        // )
        .use(
          new Selection({
            rubberband: true,
            showNodeSelectionBox: true,
          })
        )
        // .use(
        //   new Scroller({
        //     enabled: true,
        //   })
        // )
        .use(new Snapline())
        .use(new Keyboard())
        .use(new Clipboard())
        .use(new History());
      // #endregion
      // 注册自定义节点
      registerNodes();
      // 给画布绑定事件
      bindGraphEvents(graph);
    };
    // 开始创建拖拽的节点
    const startCreateNode = (e, item) => {
      const disabled = state.dropedNodes.some((it) => it.data.id === item.id);
      if (!disabled) {
        const node = graph.createNode({
          shape: "custom-vue-node",
          data: {...item,refreshData: state.refreshData},
          // tools: ["button-remove"],
        });
        dnd.start(node, e);
      }
    };
    // 为画布绑定事件
    const bindGraphEvents = (graph) => {
      // #region 快捷键与事件
      graph.bindKey(["meta+c", "ctrl+c"], () => {
        const cells = graph.getSelectedCells();
        if (cells.length) {
          graph.copy(cells);
        }
        return false;
      });
      graph.bindKey(["meta+x", "ctrl+x"], () => {
        const cells = graph.getSelectedCells();
        if (cells.length) {
          graph.cut(cells);
        }
        return false;
      });
      graph.bindKey(["meta+v", "ctrl+v"], () => {
        if (!graph.isClipboardEmpty()) {
          const cells = graph.paste({ offset: 32 });
          graph.cleanSelection();
          graph.select(cells);
        }
        return false;
      });

      // undo redo
      graph.bindKey(["meta+z", "ctrl+z"], () => {
        if (graph.canUndo()) {
          graph.undo();
        }
        return false;
      });
      graph.bindKey(["meta+shift+z", "ctrl+shift+z"], () => {
        if (graph.canRedo()) {
          graph.redo();
        }
        return false;
      });

      // select all
      graph.bindKey(["meta+a", "ctrl+a"], () => {
        const nodes = graph.getNodes();
        if (nodes) {
          graph.select(nodes);
        }
      });

      // delete
      graph.bindKey("backspace", () => {
        const cells = graph.getSelectedCells();
        if (cells.length) {
          graph.removeCells(cells);
        }
      });

      // zoom
      graph.bindKey(["ctrl+1", "meta+1"], () => {
        const zoom = graph.zoom();
        if (zoom < 1.5) {
          graph.zoom(0.1);
        }
      });
      graph.bindKey(["ctrl+2", "meta+2"], () => {
        const zoom = graph.zoom();
        if (zoom > 0.5) {
          graph.zoom(-0.1);
        }
      });

      // 控制连接桩显示/隐藏
      const showPorts = (ports, show) => {
        for (let i = 0, len = ports.length; i < len; i += 1) {
          ports[i].style.visibility = show ? "visible" : "hidden";
        }
      };
      graph.on("node:mouseenter", () => {
        const ports = unref(graphEl).querySelectorAll(".x6-port-body");
        showPorts(ports, true);
      });
      graph.on("node:mouseleave", () => {
        const ports = unref(graphEl).querySelectorAll(".x6-port-body");
        showPorts(ports, false);
      });
      // #endregion

      // 移入节点添加删除按钮
      graph.on("node:mouseenter", ({ node }) => {
        // 总 字节点不可删除
        if (node.data?.nodeName !== "rootName") {
          node.addTools({
            name: "button-remove",
            args: {
              onClick({ view }) {
                state.dropedNodes = state.dropedNodes.filter(
                  (item) => item.data.id !== node.data.id
                );
                graph.removeCell(view.cell);
              },
            },
          });
        }
      });
      // 鼠标移开时移除删除按钮
      graph.on("node:mouseleave", ({ node }) => {
        node.removeTools(); // 删除所有的工具
      });

      graph.on("edge:mouseenter", ({ edge }) => {
        edge.addTools({
          name: "button-remove",
        });
      });
      // 鼠标移开时移除删除按钮
      graph.on("edge:mouseleave", ({ edge }) => {
        edge?.removeTools(); // 删除所有的工具
      });

      // 点击空白区域 取消交换
      graph.on("blank:click", () => {
        state.refreshData = [];
        refreshNodesData()
      });
    };
    // 注册自定义节点
    const registerNodes = () => {
      // 注册vue类型的节点
      register({
        shape: "custom-vue-node",
        width: 50,
        height: 50,
        component: customVueNodeVue,
        ports: { ...ports },
      });
    };
    onMounted(() => {
      init();
    });
    return {
      state,
      asideEl,
      graphEl,
      exportData,
      startCreateNode,
    };
  },
};
</script>
<style lang="scss" scoped>
.container {
  height: 100vh;
  display: flex;
  position: relative;
  .button {
    position: absolute;
    right: 20px;
    top: 20px;
  }
  .aside {
    width: 200px;
    flex-shrink: 0;
    height: 100%;
    box-shadow: 1px 0 1px rgba($color: #000000, $alpha: 0.1);
    position: relative;
    padding: 0 10px;
    box-sizing: border-box;
    .title {
      padding: 10px 0;
    }
    .item {
      display: flex;
      justify-content: flex-start;
      align-items: center;
      margin-bottom: 10px;
      user-select: none;
      cursor: grab;
      .left {
        flex: 1;
        display: flex;
        align-items: center;
        background-color: #5f95f0;
        padding: 0 10px;
        height: 50px;
        $s: 30px;
        .image {
          width: $s;
          height: $s;
          margin-right: 10px;
          object-fit: cover;
        }
        .info {
          .name {
            font-size: 15px;
            font-weight: 600;
            color: #fff;
          }
          .sub {
            font-size: 12px;
            color: #fff;
          }
        }
      }
      &.disabled {
        cursor: not-allowed;
        .icon {
          visibility: hidden;
        }
        .left {
          filter: grayscale(0.9);
        }
      }
    }
  }
  .graph {
    flex: 1;
    height: 100%;
  }
}
</style>
